Formulas are strings, which are evaluated by an instance of the NFormulaCalculator class. The formula evaluation produces a single NVariant instance - i.e. the result of the formula evaluation. The following example shows how to evaluate formulas:
My First Formulas |
Copy Code
|
NFormulaCalculator formulaCalc = new NFormulaCalculator();
NVariant res;
formulaCalc.Formula = "10 + 20";
res = formulaCalc.Evaluate();
Console.WriteLine("10 + 20 evaluates to " + res);
formulaCalc.Formula = "10mm + 20in";
res = formulaCalc.Evaluate();
Console.WriteLine("10mm + 20in evaluates to " + res);
formulaCalc.Formula = "MIN(10, 20, 30)";
res = formulaCalc.Evaluate();
Console.WriteLine("MIN(10, 20, 30) evaluates to " + res);
formulaCalc.Formula = "SUM(10, 20, 30)";
res = formulaCalc.Evaluate();
Console.WriteLine("SUM(10, 20, 30) evaluates to " + res);
|
You can use operators and functions in formulas to perform diverse operations on values of different types.
The rest of this topic focuses on the formula basics, variants and the operators.
See Functions for more information about the available functions.
A valid formula is such a string, which is lexically and grammatically correct.
Formulas are constructed from tokens. Tokens are such substrings from the initial formula string, which cannot be broken into other tokens. A lexically correct formula consists of the following set of tokens:
- Parenthesis - the '(' and ')' characters, which are used to change the operators privilege and enclose the functions arguments.
- Curly Brackets - the '{' and '}' characters, which are used to create a block scope in the formula.
- Square Brackets - the '[' and ']' characters, which are used to enclose measurement units.
- Operators - the predefined operators, which you can use (+,-,*,\,<,> etc.)
- Commas - the ',' character, which is used to separate the function arguments.
- Semi Colons - the ';' character, which is used to separate the statements in block scope or the root scope.
- Number Constants - these are integer and floating point number constants. Following are some correctly formatted numbers:
12; 0.1; 145.23; 7.5E-17; 8.234E+13
- String Constants - - these are substrings of the formula, which are enclosed in " (quote) characters. Following are some correctly formatted strings:
="hello"; ="world"
- Identifiers - an identifier is such a sub-string from the formula that starts with a letter, '_' or '$' sign and is followed by a sequence of letters, digits, '_', '$' and '.' chars.
The formula engine first splits the input formula to a valid set of tokens, that it further processes to construct a grammatically valid tree representation of the formula. The formula tree consists of the following types of elements:
- Root Element
The root element is the root of the elements tree and is created by default. It is a scope element, meaning that you can use the ';' (semi-colon) to separate statements in the root scope.
- Block Elements
These are scope elements that may contain multiple statements separated with the ';' (semi-colon) char. Block elements are created for the parts of the formula that are enclosed in '{' '}' (curly brackets).
- Constant Elements
These are formula tree elements that always evaluate to a constant NVariant value. Constant elements are created for number and string tokens, as well as for identifiers that are equal to the true and false boolean value keywords. For example:
- constant numbers: 12; 0.1; 145.23; 7.5E-17; 8.234E+13
- constant strings: "hello"; "world"
- constant booleans: true; false
- Standard Operator Elements
Standard operator elements are created for tokens that were recognized as valid operator tokens (+,-,*,\,<,> etc.). Operator elements perform a specific operation upon two arguments (binary operators) or single argument (unary operators). For example:
- binary operators: 2 + 2 will evaluate to 4
- unary operators: -2 will evaluate to -2
- Unit Operator Elements
Unit operator elements are created for formula substrings that are enclosed in '[' ']' (square brackets). Unit operator elements are unary operators that multiple a value with a certain measurement unit. A number value associated with a measurement unit is called measure. Measurement unit algebra rules apply for measures. For example:
10 [mm] + 20 [in] evaluates to 518 [mm]
- Function Elements
Function elements are created for identifier tokens that reference a valid function name and are followed by the '(' (left parenthesis token). Supported is a large set of mathematical, trigonometric, logical, text, date-time and bitwise functions. Function arguments must be enclosed in parenthesis and divided by commas (see Functions). For example:
MIN(10, 20) - evaluates to 10; COS(0) - evaluates to 1.
- Local Variable Elements
Local variables are identifier tokens that reside on the left side of the assignment operator. Each local variable is considered declared for a specific scope.
- Global Variable Elements
Variables elements are formula tree elements that evaluate to a variable NVariant value. The formula engine dynamically queries for variables and their values via the INVariableProvider interface.
The formula evaluation works with variants. The result of each formula evaluation is a variant too (e.g. a value of certain type - see Variants below).
Variants are represented by instances of the NVariant structure. Variants are value-type pairs, which facilitate the type conversion of values, as well as the operations with values of different type.
When variants are used in functions or operators that require a value of a certain type, variants are automatically converted to a value of this type. There are several distinct types of variants:
- Empty
An empty variant holds no value. It is equivalent to null or DBNull. An empty variant can only be converted to string (as the "EMPTY()" string constant).
- Boolean
Holds a boolean true or false value. Boolean variants can be automatically converted to:
- string - as the "false" and "true" string constants respectively.
- number - as the 0 or 1 number values respectively.
- Number
Holds a floating point or integer number, with some precision (for example an Int32 number variant holds an integer value in 32 bits). When performing arithmetic operations number variants automatically change their type to ensure that they can hold the resulting value. Converting floating point numbers to integer numbers removes the decimal portion of the value. You can format numbers with the FORMAT function. Number variants can be automatically converted to:
- string - by default used is the en-US culture.
- boolean - as boolean false if number is zero, as boolean true otherwise.
- String
Holds a text string. Strings can be automatically converted to:
- boolean - to boolean false, if string is equal to "false", "FALSE" or "False" and to boolean true if string is equal to "true", "TRUE" or "true".
- number - if the string contains a valid number (by default used is the en-US culture)
- date time - if the string contains a valid date time (by default used is the en-US culture)
- Date Time
Holds a date time value. Date-time variants can be converted only to string (by default using the en-US culture) and can be formatted with the FORMAT function.
- Measure
Holds a number value associated with a certain measurement unit. Measurement unit algebra rules apply to arithmetic operations with measures. Measures can be converted to:
- strings - the value part by default uses the en-US culture, followed by the string representation of the unit.
- booleans - as boolean false if value part is zero, as boolean true otherwise.
- numbers - only if the unit has no dimension (like the radians unit for example).
- Array - holds an array of other variants.
The power of variants is their ability to provide transparent handling of type and measurement unit conversion so that you can perform common mathematical and logical operations, without caring for the actual type of the involved values. For example:
Variants Example |
Copy Code
|
NVariant res1 = new NVariant(20) + new NVariant(30.0);
Console.WriteLine("Addition operator result with numbers: " + res1.ToString());
NVariant res2 = new NVariant(10, NUnit.Millimeter) + new NVariant(20, NUnit.Inch);
Console.WriteLine("Addition operator result with measures: " + res2.ToString());
NVariant res3 = new NVariant(10, NUnit.Meter) / new NVariant(20, NUnit.Second);
Console.WriteLine("Division operator result with measures: " + res3.ToString());
NVariant res4 = new NVariant(10, NUnit.Millimeter) > new NVariant(8, NUnit.Inch);
Console.WriteLine("Comparision operator result: " + res4.ToString());
// Output is:
// Addition operator result with numbers: 50
// Addition operator result with measures: 518 [mm]
// Division operator result with measures: 0.5 [m/s]
// Comparison operator result: false
|
You can use the NVariant structure to perform arithmetics and comparisons of values of different type. It is especially useful when dealing with measures.
You can use operators in formulas to perform arithmetic, comparison, logical and bitwise operations as described by the following table:
Arithmetic Operators
|
Syntax |
Name |
Description |
Examples |
+ |
Unary plus |
Does nothing - it is defined for completeness with the unary minus.
Argument should be Number, Measure, Array or Empty.
Supports array chaining. |
+10
Returns: 10 |
- |
Unary minus |
Used to establish a number with a negative sign.
Argument should be Number, Measure, Array or Empty.
Supports array chaining. |
-10
Returns: -10
-ARRAY(10, 12)
Returns: ARRAY(-10, -12) |
^ |
Exponentiation |
Raises left to the power of right.
Left should be a number or a measure.
Right should be a number.
If either one is empty the result is empty variant.
Supports array chaining. |
10 ^ 2
Returns: 100
ARRAY(10, 12) ^ 2
Returns: ARRAY(100, 144) |
* |
Multiplication |
Multiplies left by right.
Both arguments should be numbers or measures.
If either one is empty the result is empty variant.
Supports array chaining. |
10 * 2
Returns: 20
ARRAY(10, 12) * 2
Returns: ARRAY(20, 24) |
/ |
Division |
Divides left by right.
Both arguments should be numbers or measures.
If either one is empty the result is empty variant.
Supports array chaining.
|
10 / 2
Returns: 5
ARRAY(10, 12) / 2
Returns: ARRAY(5, 6) |
+ |
Addition |
Adds right to left.
If either argument is a String, performs string concatenation.
If left is DateTime, the right must be a measure from the time dimension.
If left or right argument is measure, the opposite argument needs to be from the same measure dimension. Result is expressed in left argument unit of measure.
Supports array chaining.
|
10 + 2
Returns: 12
ARRAY(10, 12) + 2
Returns: ARRAY(12, 14)
ARRAY(10, 12) + ARRAY(12, 23)
Returns: ARRAY(22, 35)
10 + "Nevron"
Returns: "10Nevron"
|
- |
Subtraction |
Subtracts right from left.
If left is DateTime, the right must be a measure from the time dimension.
If left or right argument is measure, the opposite argument needs to be from the same measure dimension. Result is expressed in left argument unit of measure.
Supports array chaining. |
10 - 2
Returns: 8
ARRAY(10, 12) - 2
Returns: ARRAY(8, 10)
ARRAY(10, 12) - ARRAY(12, 23)
Returns: ARRAY(-2, -11) |
Comparison Operators
|
Syntax |
Name |
Description |
Examples |
> |
Greater than |
Returns true if left is greater than right. Otherwise returns false.
Supports array chaining. |
10 > 2
Returns: true |
< |
Less than |
Returns true if left is less than right. Otherwise returns false.
Supports array chaining. |
10 < 2
Returns: false |
>= |
Greater than or equal to |
Returns true if left is greater than or equal to right. Otherwise returns false.
Supports array chaining. |
10 >= 2
Returns: true |
<= |
Less than or equal to |
Returns true if left is less than or equal to right. Otherwise returns false.
Supports array chaining. |
10 <= 2
Returns: false |
== |
Equal to |
Returns true if left is equal to right. Otherwise returns false.
Supports array chaining. |
10 = 2
Returns: false |
!= |
Not equal to |
Returns true if left is not equal to right. Otherwise returns false.
Supports array chaining. |
10 != 2
Returns: true |
Logical operators
|
Syntax |
Name |
Description |
Examples |
&& |
Logical AND |
Returns true if both left and right are true, otherwise returns false.
Supports array chaining. |
true && false
Returns: false |
|| |
Logical OR |
Returns true if either left or right is true, otherwise returns false.
Supports array chaining. |
true || false
Returns: true |
! |
Logical NOT |
Unary operator, that returns the inverse boolean value of its argument.
Supports array chaining. |
!true
Returns: false |
Bitwise Operators
|
& |
Bitwise AND |
Returns a 32-bit binary number in which a bit is set to 1 if the corresponding bit in both left and right are 1. Otherwise, the bit is set to 0. Supports array chaining. |
7 & 2
Returns: 2 (7 = 0111, 2 = 0010) |
| |
Bitwise OR |
Returns a 32-bit binary number in which a bit is set to 1 if the corresponding bit in either left or right is 1. Otherwise, the bit is set to 0. Supports array chaining. |
5 | 3
Returns: 7 (5 = 0101, 3 = 0011, 7 = 0111) |
~ |
Bitwise NOT |
Unary operator, that returns a 32-bit binary number in which a bit is set to 1 if the corresponding bit in the operand is 0. Otherwise, the bit is set to 0. Supports array chaining. |
~1
Returns: 2 ^ 32 - 1 |
Assignment Operators
|
Syntax |
Name |
Description |
Examples |
= |
Assignment |
Assigns a value to a local variable. If a local variable is not yet declared, declares a new one. Returns the value of the local variable. |
a=6; b=3; a+b;
Returns: 9 |
+= |
Addition Assignment |
Assigns the value to a local variable to be equal to sum of the current value and the result of the right argument evaluation.
Does not declare a new local variable, if it does not exist (i.e. the local variable should first reside on the left side of an assignment operator to be declared). Returns the new value of the local variable. |
a=6; a+=3
Returns: 9 |
-= |
Subtraction Assignment |
Assigns the value to a local variable to be equal to the subtraction of the current value and the result of the right argument evaluation.
Does not declare a new local variable, if it does not exist (i.e. the local variable should first reside on the left side of an assignment operator to be declared). Returns the new value of the local variable. |
a=6; a-=3
Returns: 3 |
-= |
Multiplication Assignment |
Assigns the value to a local variable to be equal to the multiplication of the current value and the result of the right argument evaluation.
Does not declare a new local variable, if it does not exist (i.e. the local variable should first reside on the left side of an assignment operator to be declared). Returns the new value of the local variable. |
a=6; a*=3
Returns: 18 |
-= |
Division Assignment |
Assigns the value to a local variable to be equal to the division of the current value and the result of the right argument evaluation.
Does not declare a new local variable, if it does not exist (i.e. the local variable should first reside on the left side of an assignment operator to be declared). Returns the new value of the local variable. |
a=6; a/=3
Returns: 2 |
-= |
Exponentiation Assignment |
Assigns the value to a local variable to be equal to the exponentiation of the current value and the result of the right argument evaluation.
Does not declare a new local variable, if it does not exist (i.e. the local variable should first reside on the left side of an assignment operator to be declared). Returns the new value of the local variable. |
a=6; a^=3
Returns: 216 |
Array chaining, in the context of operators is the ability to pass as argument(s) an array variant. For example:
ARRAY(10,20) + 5, evaluates to ARRAY(15,25).
ARRAY(10,20) + ARRAY(5, 15, 30), evaluates to ARRAY(15,35).
-ARRAY(10,20), evaluates to ARRAY(-10,-20)
There is a certain precedence assigned to each operator. If an expression contains several operators, which are not enclosed in parentheses, the operator with the highest precedence is evaluated first. For example:
10 + 20 * 2 - evaluates to 50, because the multiplication operator will be evaluated first (it has a higher precedence than the addition operator).
(10 + 20) * 2 - evaluates to 60, since the parentheses override the default way in which the expression is evaluated.
The following table summarizes the operators precedence, in high to low order.
Category |
Operators |
Primary |
Unit Operators ([mm], [in], [m], [s], [m/s] etc.) |
Unary |
Unary Plus (+), Unary Minus (-), Logical Not (!), Bitwise Not (~) |
Power |
Exponentiation (^) |
Multiplicative |
Multiplication (*), Division (/) |
Additive |
Addition (+), Subtraction (-) |
Relational |
Greater Than (>), Less Than (<), Greater Than Or Equal To (>=), Less Than Or Equal To (<=) |
Equality |
Equal To (=), Not Equal To (!=) |
Bitwise AND |
Bitwise AND (&) |
Bitwise OR |
Bitwise OR (|) |
Logical AND |
Logical AND (&&) |
Logical OR |
Logical OR (||) |
Assignment |
Assignment (=) |
There is also a left or right associativity assigned to each operator. The associativity determines the execution order of operators with the same precedence. The Assignment (=) and the Exponentiation (^) operators have right associativity. All other operators have left associativity.
To demonstrate the effect of right versus left associativity consider the following expression:
5^2^3 - evaluates to 390625 (5^(2^3) = 390625), which is different than (5^2)^3=15625, because the Exponentiation has right associativity.